package com.market.service.impl;

import com.market.constants.Constants;
import com.market.dao.StrategyAwardDao;
import com.market.dao.StrategyDao;
import com.market.dao.StrategyRuleDao;
import com.market.domain.entity.StrategyAwardEntity;
import com.market.domain.entity.StrategyEntity;
import com.market.domain.entity.StrategyRuleEntity;
import com.market.domain.po.Strategy;
import com.market.domain.po.StrategyAward;
import com.market.domain.po.StrategyRule;
import com.market.enums.ResponseCode;
import com.market.exception.AppException;
import com.market.redis.RedisService;
import com.market.service.StrategyArmoryService;
import com.market.service.StrategyDispatch;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.util.*;

@Service
@Slf4j
public class StrategyArmoryServiceImpl implements StrategyArmoryService , StrategyDispatch {

    @Resource
    private RedisService redisService;
    @Resource
    private StrategyAwardDao strategyAwardDao;
    @Resource
    private StrategyDao strategyDao;
    @Resource
    private StrategyRuleDao strategyRuleDao;


    @Override
    public boolean assembleLotteryStrategy(Long strategyId) {

        //查询策略配置
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_KEY+strategyId;
        List<StrategyAwardEntity> strategyAwardEntities =redisService.getValue(cacheKey);
        if(strategyAwardEntities ==null ){

            List<StrategyAward> strategyAwards=strategyAwardDao.queryStrategyAwardListByStrategyId(strategyId);
            strategyAwardEntities =new ArrayList<>(strategyAwards.size());

            for(StrategyAward strategyAward :strategyAwards){
                StrategyAwardEntity strategyAwardEntity=StrategyAwardEntity.builder()
                        .strategyId(strategyAward.getStrategyId())
                        .awardId(strategyAward.getAwardId())
                        .awardCount(strategyAward.getAwardCount())
                        .awardCountSurplus(strategyAward.getAwardCountSurplus())
                        .awardRate(strategyAward.getAwardRate())
                        .build();

                strategyAwardEntities.add(strategyAwardEntity);
            }
            redisService.setValue(cacheKey,strategyAwardEntities);
        }

        assembleLotteryStrategy(String.valueOf(strategyId),strategyAwardEntities);

        //规则配置--rule_weight权重规则模型
        StrategyEntity strategyEntity = queryStrategyEntityByStrategyId(strategyId);
        String ruleWeight=strategyEntity.getRuleWeight();
        //没有权重策略
        if(null == ruleWeight) return true;

        StrategyRuleEntity strategyRuleEntity=queryStrategyRule(strategyId,ruleWeight);
        //未装配rule_value
        if(null==strategyRuleEntity){
            throw new AppException(ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getCode(),ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getInfo());
        }
        Map<String, List<Integer>> ruleWeightValueMap = strategyRuleEntity.getRuleWeightValue();
        Set<String> keys = ruleWeightValueMap.keySet();
        for (String key : keys) {
            List<Integer> ruleWeightValues = ruleWeightValueMap.get(key);
            ArrayList<StrategyAwardEntity> strategyAwardEntitiesClone=new ArrayList<>(strategyAwardEntities);
            //移除不在列表中的奖品
            strategyAwardEntitiesClone.removeIf(entity->!ruleWeightValues.contains(entity.getAwardId()));
            assembleLotteryStrategy(String.valueOf(strategyId).concat("_").concat(key),strategyAwardEntitiesClone);
        }
        return true;

    }

    public StrategyEntity queryStrategyEntityByStrategyId(Long strategyId) {

        //1. 查找策略
        String cacheKey = Constants.RedisKey.STRATEGY_KEY+ strategyId;
        StrategyEntity strategyEntity =redisService.getValue(cacheKey);
        if(null!=strategyEntity){

           return strategyEntity;
        }

        //select strategy_id, strategy_desc, rule_models
        Strategy strategy=strategyDao.queryStrategyByStrategyId(strategyId);

        strategyEntity = StrategyEntity.builder()
                .strategyId(strategy.getStrategyId())
                .strategyDesc(strategy.getStrategyDesc())
                .ruleModels(strategy.getRuleModels())
                .build();

        redisService.setValue(cacheKey,strategyEntity);
        return strategyEntity;
    }

    /**
     * 查询策略规则（select by strategy_id,rule_model）
     * @param strategyId
     * @param ruleModel
     * @return
     */
    public StrategyRuleEntity queryStrategyRule(Long strategyId, String ruleModel) {
        StrategyRule strategyRuleReq = new StrategyRule();
        strategyRuleReq.setStrategyId(strategyId);
        strategyRuleReq.setRuleModel(ruleModel);

        StrategyRule strategyRuleRes = strategyRuleDao.queryStrategyRule(strategyRuleReq);

        return StrategyRuleEntity.builder()
                .strategyId(strategyRuleRes.getStrategyId())
                .awardId(strategyRuleRes.getAwardId())
                .ruleType(strategyRuleRes.getRuleType())
                .ruleModel(strategyRuleRes.getRuleModel())
                .ruleValue(strategyRuleRes.getRuleValue())
                .ruleDesc(strategyRuleRes.getRuleDesc())
                .build();
    }

    /**
     * 抽奖策略装配
     * @param key
     * @param strategyAwardEntities
     */
    public void assembleLotteryStrategy(String key, List<StrategyAwardEntity> strategyAwardEntities){

        //获取最小概率值
        BigDecimal minAwardRate = strategyAwardEntities.stream()
                .map(StrategyAwardEntity::getAwardRate)
                .min(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);

        //获取概率总和
        BigDecimal totalAwardRate = strategyAwardEntities.stream()
                .map(StrategyAwardEntity::getAwardRate)
                .reduce(BigDecimal.ZERO,BigDecimal::add);

        //获取概率范围（分位）
        BigDecimal rateRange = totalAwardRate.divide(minAwardRate,0, RoundingMode.CEILING);
        //填充进查找表
        ArrayList<Integer> strategyAwardSearchRateTables = new ArrayList<>(rateRange.intValue());

        for(StrategyAwardEntity strategyAward:strategyAwardEntities){
            Integer awardId = strategyAward.getAwardId();
            BigDecimal awardRate =strategyAward.getAwardRate();
            //位数*概率=每种概率值需要放进查找表的数量,循环1填充1
            for(int i=0;i<rateRange.multiply(awardRate).setScale(0,RoundingMode.CEILING).intValue();i++){
                strategyAwardSearchRateTables.add(awardId);
            }
        }

        //对查找表乱序处理
        Collections.shuffle(strategyAwardSearchRateTables);

        //
        HashMap<Integer, Integer> shuffleStrategyAwardSearchRateTables = new HashMap<>();
        for(int i=0;i<strategyAwardSearchRateTables.size();i++){
            shuffleStrategyAwardSearchRateTables.put(i,strategyAwardSearchRateTables.get(i));
        }

        //存入redis
        //策略范围值
        redisService.setValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY+key,rateRange.intValue());
        //概率查找表
        Map<Integer,Integer> cacheRateTable=redisService.getMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY+key);
        cacheRateTable.putAll(shuffleStrategyAwardSearchRateTables);

    }

    @Override
    public Integer getRandomAwardId(Long strategyId) {

        int rateRange=redisService.getValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY+strategyId);

        return redisService.getFromMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY+strategyId,new SecureRandom().nextInt(rateRange));

    }

    @Override
    public Integer getRandomAwardId(Long strategyId, String ruleWeightValue) {
        String key = String.valueOf(strategyId).concat("_").concat(ruleWeightValue);

        int rateRange=redisService.getValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY+key);

        return redisService.getFromMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY+key,new SecureRandom().nextInt(rateRange));

    }
}
